/**
 * 20210819:升级修改【three.js r120-r131】
 * 20220304:[require] to [import]...jordia
 * 20220704:extract method [_getRayComponents] to calculate origin and direction of ray
 */
import {MathUtils,Vector3,Matrix4} from 'three';

const MIN_SCALE=1e-6;

/**
 * 约束基类
 * @param values {object}
 freeSnapEnabled:false 是否开启自由吸附（将按固定单位移动）
 freeSnapValue:0.1 自由吸附的值
 freeSnapOffset:0 自由吸附偏移值
 */
export default class DragLimit{
    constructor(values={}){
        this._values=values;
        this._init();
    }

    /**
     * 获取约束类型
     * @returns {string}
     */
    get type(){
        return 'dragLimit';
    }

    /**
     * 唯一ID
     * @returns {null|string}
     */
    get uuid(){
        return this._uuid;
    }

    //---------------------------------------------------------------------------------------------

    get freeSnapEnabled(){
        return this._freeSnapEnabled;
    }
    set freeSnapEnabled(bol){
        this._freeSnapEnabled=bol;
    }
    get freeSnapValue(){
        return this._freeSnapValue;
    }
    set freeSnapValue(number){
        this._freeSnapValue=number;
    }
    get freeSnapOffset(){
        return this._freeSnapOffset;
    }
    set freeSnapOffset(number){
        this._freeSnapOffset=number;
    }

    //------------------------------------------------------------------------------------------------

    /**
     * 约束行为
     * @param object
     * @param v
     * @param callback
     * @param camera
     */
    modifyMove(object,v,callback,camera){}

    /**
     * 销毁
     */
    dispose(){
        this._freeSnapEnabled=null;
        this._freeSnapValue=null;
        this._freeSnapOffset=null;

        this._uuid=null;
        this._matPWorld=null;
        this._rayObjectWorldInverse=null;
        this._matPInverse=null;
        this._matP=null;
        this._scaleP=null;
        this._scaleTV=null;
        this._translateP=null;
        this._tV0=null;
    }

    _transformVector(object,v){
        // this._matPWorld.multiplyMatrices(object.matrixWorld,this._rayObjectWorldInverse.getInverse(object.matrix));
        this._matPWorld.multiplyMatrices(object.matrixWorld,this._rayObjectWorldInverse.copy(object.matrix).invert());//20210819
        this._matP.extractRotation(this._matPWorld);
        this._scaleP.setFromMatrixScale(this._matPWorld);
        this._scaleTV.set(
            1/(this._scaleP.x===0?MIN_SCALE:this._scaleP.x),
            1/(this._scaleP.y===0?MIN_SCALE:this._scaleP.y),
            1/(this._scaleP.z===0?MIN_SCALE:this._scaleP.z)
        );
        let objPos=this._tV0;
        if(object.parent) {
            object.parent.getWorldPosition(objPos);
        }else{
            objPos.set(0,0,0);
        }
        this._translateP.copy(objPos);
        v.sub(this._translateP);
        // v.applyMatrix4(this._matPInverse.getInverse(this._matP));
        v.applyMatrix4(this._matPInverse.copy(this._matP).invert());//20210819
        v.multiply(this._scaleTV);
        return v;
    }
    /**
     * 20220704
     * @param {*} camera 
     * @param {*} v 
     * @param {*} originOut 
     * @param {*} directionOut 
     */
    _getRayComponents(camera,v,originOut,directionOut){
        camera.getWorldPosition(originOut);
        if(camera.isPerspectiveCamera){
            directionOut.subVectors(v, originOut).normalize();
        }else if(camera.isOrthographicCamera){
            const len=originOut.distanceTo(v);
            camera.getWorldDirection(directionOut);
            directionOut.multiplyScalar(len);
            originOut.subVectors(v,directionOut);
            directionOut.normalize();
        }else{
            console.warn('unsupport camera type!');
        }
    }

    _init(){
        this._initVariable();
    }
    _initVariable(){
        this._freeSnapEnabled=this._values.freeSnapEnabled||false;
        this._freeSnapValue=this._values.freeSnapValue||0.1;
        this._freeSnapOffset=this._values.freeSnapOffset||0;

        this._uuid=MathUtils.generateUUID();
        this._matPWorld=new Matrix4();
        this._rayObjectWorldInverse=new Matrix4();
        this._matPInverse=new Matrix4();
        this._matP=new Matrix4();
        this._scaleP=new Vector3();
        this._scaleTV=new Vector3();
        this._translateP=new Vector3();
        this._tV0=new Vector3();
    }
}